<?php
/**
 * 支付宝
 */

namespace utils;

use alipay\AopCertClient;
use Alipay\EasySDK\Kernel\Factory;
use Alipay\EasySDK\Kernel\Util\ResponseChecker;
use Alipay\EasySDK\Kernel\Config;
use alipay\request\AlipayFundTransUniTransferRequest;
use alipay\request\AlipayTradeAppPayRequest;
use alipay\request\AlipayTradeQueryRequest;
use exceptions\ApiException;
use think\exception\HttpException;
use think\facade\Db;

class AliPay
{
    protected $conf;
    public $pay_v = 1; // 1 通用 2 Easy

    protected static $appCertPath = ''; //应用公钥证书
    protected static $alipayCertPath = '';//支付宝公钥证书
    protected static $rootCertPath = '';//支付宝根证书
    protected static $appId = 0; //APPID
    protected static $rsaPrivateKey = ''; //支付宝应用私钥
    protected static $gatewayUrl = 'https://openapi.alipay.com/gateway.do';//网关地址
    protected static $signType = 'RSA2';
    protected static $postCharset = 'utf-8';
    protected static $format = 'json';
    protected static $notifyUrl = 'http://test.17jbshop.com/aliResult';
    public static $mchid = '';
    private $class;

    public function __construct($type = 'pay')
    {
        self::payConfig($type);
        if (in_array($type, ['pay', 'withdrawal', 'offline'])) {
            if ($this->pay_v == 1) {
                $this->class = new AopCertClient();
                $this->class->gatewayUrl = self::$gatewayUrl;
                $this->class->appId = self::$appId;
                $this->class->rsaPrivateKey = self::$rsaPrivateKey;
                $this->class->format = self::$format;
                $this->class->postCharset = self::$postCharset;
                $this->class->signType = self::$signType;
                //调用getPublicKey从支付宝公钥证书中提取公钥
                $this->class->alipayrsaPublicKey = $this->class->getPublicKey(self::$alipayCertPath);
                //是否校验自动下载的支付宝公钥证书，如果开启校验要保证支付宝根证书在有效期内
                $this->class->isCheckAlipayPublicCert = true;
                //调用getCertSN获取证书序列号
                $this->class->appCertSN = $this->class->getCertSN(self::$appCertPath);
                //调用getRootCertSN获取支付宝根证书序列号
                $this->class->alipayRootCertSN = $this->class->getRootCertSN(self::$rootCertPath);
                //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.open.public.template.message.industry.modify
                $this->class->alipayPublicKey = self::$alipayCertPath;
            } else {
                Factory::setOptions(self::getOptions());
            }
        }
    }

    /**
     * 支付宝统一下单（通用版本sdk）
     * @param array $param
     * @return array
     */
    public function unifiedOrder(array $param)
    {
        $request = new AlipayTradeAppPayRequest();
        $bizContent = [
            'total_amount' => $param['total_amount'],
            'subject' => $param['body']?:'iphone 面膜',
            'out_trade_no' => $param['out_trade_no'],
        ];
        $request->setNotifyUrl(self::$notifyUrl);
        $request->setBizContent(json_encode($bizContent, JSON_UNESCAPED_UNICODE));
        $result = $this->class->sdkExecute($request);
        if (!empty($result))
            return ['flag' => true, 'msg' => '调用成功', 'data' => ['result' => $result]];
        else
            return ['flag' => false, 'msg' => "统一下单失败"];
    }

    /**
     * 交易订单查询
     * @param string $out_trade_no
     * @return bool
     * @throws \Exception
     */
    public function tradeQuery(string $out_trade_no)
    {
        $request = new AlipayTradeQueryRequest();
        $bizContent = [
            'out_trade_no' => $out_trade_no,
        ];
        $request->setBizContent(json_encode($bizContent, JSON_UNESCAPED_UNICODE));
        $result = $this->class->execute($request);
        $result = object_array($result);
        if (isset($result['alipay_trade_query_response']) && $result['alipay_trade_query_response']['msg'] == 'Success' && $result['alipay_trade_query_response']['trade_status'] == 'TRADE_SUCCESS') {
            Db::name('payment')->where('out_trade_no', $out_trade_no)->update(['state' => 6, 'pay_at' => date('Y-m-d H:i:s'), 'trade_no' => $result['alipay_trade_query_response']['trade_no']]); // 更改订单状态
            return !0;
        } else {
            return !1;
        }
    }

    /**
     * 交易订单查询（线下）
     * @param string $out_trade_no
     * @return bool
     * @throws \Exception
     */
    public function tradeQueryOffline(string $out_trade_no)
    {
        $request = new AlipayTradeQueryRequest();
        $bizContent = [
            'trade_no' => $out_trade_no,
        ];
        $request->setBizContent(json_encode($bizContent, JSON_UNESCAPED_UNICODE));
        $result = $this->class->execute($request);
        $result = object_array($result);
        if (isset($result['alipay_trade_query_response']) && $result['alipay_trade_query_response']['msg'] == 'Success' && $result['alipay_trade_query_response']['trade_status'] == 'TRADE_SUCCESS') {
            $obj = Db::name('payment')->where('trade_no', $out_trade_no);
            $payment_info = $obj->findOrFail();
            if ($payment_info['total_fee'] != $result['alipay_trade_query_response']['total_amount']) {
                throw new HttpException(500, '金额不符');
            }
            $obj->save(['state' => 6, 'pay_at' => date('Y-m-d H:i:s'), 'out_trade_no' => $result['alipay_trade_query_response']['out_trade_no']]); // 更改订单状态
            return !0;
        } else {
            return !1;
        }
    }

    /**
     * 支付宝统一下单（Easy版本sdk）
     * @param array $param
     * @return array
     */
    public function easyUnifiedOrder(array $param)
    {
        try {
            $result = Factory::payment()->common()->create($param['body'], $param['out_trade_no'], $param['total_amount'], null);
            $responseChecker = new ResponseChecker();
            //3. 处理响应或异常
            if ($responseChecker->success($result)) {
                return ['flag' => true, 'msg' => '调用成功', 'data' => $result];
            } else {
                return ['flag' => false, 'msg' => "调用失败，原因：" . $result->msg . "，" . $result->subMsg . PHP_EOL];
            }
        } catch (\Exception $e) {
            return ['flag' => false, 'msg' => "调用失败，" . $e->getMessage() . PHP_EOL];
        }
    }

    /**
     * 提现
     * @param array $param
     * @return array
     * @throws \Exception
     */
    public function withdrawal(array $param)
    {
        $bizContent = [
            'out_biz_no' => time() . rand(10000, 99999) . $param['user_id'],
            'trans_amount' => $param['practical_money'],
            'product_code' => 'TRANS_ACCOUNT_NO_PWD',
            'biz_scene' => 'DIRECT_TRANSFER',
            'order_title' => '用户提现',
            'payee_info' => [
                'identity_type' => 'ALIPAY_LOGON_ID',
                'identity' => $param['zfb_number'],
                'name' => $param['zfb_name'],
            ],
            'remark' => '用户提现'
        ];
        $request = new AlipayFundTransUniTransferRequest();
        $request->setBizContent(json_encode($bizContent, JSON_UNESCAPED_UNICODE));
        $result = $this->class->execute($request);
        if (!$result) {
            return ['flag' => false, 'msg' => '系统出错'];
        }
        $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
        $resultCode = $result->{$responseNode}->code;
        if (!empty($resultCode) && $resultCode == 10000) {
            return ['flag' => true, 'data' => ['out_biz_no' => $result->{$responseNode}->out_biz_no]];
        } else {
            return ['flag' => false, 'msg' => $result->{$responseNode}->sub_msg];
        }
    }

    /**
     * 验签
     * @param array $param
     * @return bool
     */
    public function checkSign(array $param)
    {
        try {
            if ($this->pay_v == 1) {
                $obj = new AopCertClient();
                $param['fund_bill_list'] = html_entity_decode($param['fund_bill_list']); // 处理数据中存在&quot;
                return $obj->rsaCheckV1($param, self::$alipayCertPath, 'RSA2');
            } else {
                return Factory::payment()->common()->verifyNotify($param);
            }
        } catch (\Exception $e) {
            return false;
        }
    }

    /**
     * 公共参数
     * @return Config
     */
    static protected function getOptions()
    {
        $options = new Config();
        $options->protocol = 'https';
        $options->gatewayHost = self::$gatewayUrl;
        $options->signType = self::$signType;
        $options->appId = self::$appId;
        $options->merchantPrivateKey = self::$rsaPrivateKey; // 应用私钥
        $options->alipayCertPath = self::$alipayCertPath; // 支付宝公钥证书文件路径
        $options->alipayRootCertPath = self::$rootCertPath; // 支付宝根证书文件路径
        $options->merchantCertPath = self::$appCertPath; // 应用公钥证书文件路径
        return $options;
    }

    /**
     * 支付宝配置
     * @param $type
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     */
    static protected function payConfig($type)
    {
        if ($type == 'pay') {
            $conf = Db::name('pay_config')->field('app_id,private_key,app_cert_path,pay_root_cert,pay_cert_path,mchid')->where(['channel' => 1, 'id' => 1])->findOrFail();
        } else if ($type == 'withdrawal') { // 提现使用渔夫科技的支付配置
            $conf = Db::name('pay_config')->field('app_id,private_key,app_cert_path,pay_root_cert,pay_cert_path,mchid')->where(['channel' => 1, 'is_withdrawal' => 1])->findOrFail();
        } else if ($type == 'offline') { // 线下付款
            $conf = Db::name('pay_config')->field('app_id,private_key,app_cert_path,pay_root_cert,pay_cert_path,mchid')->where(['channel' => 1, 'is_offline' => 1])->findOrFail();
        }
        if (empty($conf))
            throw new HttpException(500, '支付宝配置错误');
        self::$appCertPath = $conf['app_cert_path']; //应用公钥证书
        self::$alipayCertPath = $conf['pay_cert_path'];//支付宝公钥证书
        self::$rootCertPath = $conf['pay_root_cert'];//支付宝根证书
        self::$appId = $conf['app_id']; //APPID
        self::$rsaPrivateKey = $conf['private_key']; //支付宝应用私钥
        self::$mchid = $conf['mchid']; // 商户id
    }
}